package com.google.code.retrobox.editor.tracker;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import com.google.code.retrobox.editor.sprites.SpriteEditor;
import com.google.code.retrobox.editor.sprites.SpriteRenderer;
import com.google.code.retrobox.editor.tracker.model.Pattern;

public class TrackerIO {

	private static final int[] NOTE_MAP = { 16, 17, 18, 19, 20, 21, 23, 24, 26,
			28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69,
			73, 78, 82, 87, 93, 98, 104, 110, 116, 123, 131, 139, 147, 156,
			165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330,
			349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 698, 740,
			784, 831, 880, 932, 988, 1046, 1109, 1175, 1245, 1319, 1397, 1480,
			1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2760,
			3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978 };

	public void exportToHeader(final File file, final Pattern pattern,
			final int tempo) throws Exception {
		try {
			String path = file.getAbsolutePath();
			if (!path.endsWith(".h")) {
				path = path + ".h";
			}
			int tick = 0;
			BufferedWriter out = new BufferedWriter(new FileWriter(path));
			out.write("// Auto generated sound header file. RetroBox IDE. \n\n"
					+ "const unsigned char sound1 [] PROGMEM =  { \n");

			String data[][] = pattern.getData();

			int length = data.length;

			for (int i = 0; i < length; i++) {
				int note = 0;
				if (Integer.parseInt(data[i][Pattern.TRACK_0_NOTE]) == 999) {
					note = getPrevNoteValue(i, pattern);
				} else {
					note = NOTE_MAP[Integer
							.parseInt(data[i][Pattern.TRACK_0_NOTE])];
				}
				out.write(String.format("%d, %s,\n",
						convertTrackerTickToVideoTime(Integer
								.parseInt(data[i][Pattern.TRACK_0_SEQUENCE]),
								tempo), note));

			}

			out.write("0 }");

			out.close();

		} catch (IOException e) {
			throw new Exception("Error while exporting file.", e);
		}
	}

	private int getPrevNoteValue(final int pos, final Pattern pattern) {
		int note = 0;
		int t = pos - 1;
		boolean swFound = false;
		while (t > 0 && !swFound) {
			if (!pattern.getData()[t][1].equals("999")
					&& !pattern.getData()[t][1].equals("000")) {
				note = Integer.valueOf(pattern.getData()[t][1]);
				swFound = true;
			}
			t--;
		}
		return note;
	}

	private long convertTrackerTickToVideoTime(final int tick, final int tempo) {
		double dtick = tick;
		double dtempo = tempo;
		double videoTime = ((60 / (dtempo * 4)) * dtick) / 0.02; // video frame
																	// = 50 hz
		return Math.round(videoTime);
	}

	
	private String[] rtttlNotes = {"P","C#","C","D#","D","E","F#","F","G#","G","A#","A","B" }; 
	private int[] rtttlNotesValue = {0, 2, 1, 4, 3,5,7,6,9,8,11,10,12 }; 
	private final String[] rttlNoteDuration = { "16", "32", "1", "2", "4", "8" };
    private final int[] noteTick = { 1, 1, 32, 16, 8, 2 };
    
	public void importFromRTTTL(final File file,TrackerEditor trackerEditor) throws Exception {
		/* RTTTL spec see http://merwin.bespin.org/t4a/specs/nokia_rtttl.txt */
		int octave = 5;
		int duration = 4;
		int bpm = 120;
		int tickCounter = 0;
		Pattern pattern = trackerEditor.pattern;
		try {
			FileInputStream fstream = new FileInputStream(file);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));

			String line;
			StringBuffer sb = new StringBuffer();
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
			in.close();
			String text = sb.toString();
			String[] parts = text.split(":");
			if (parts.length == 3) {
				String name = parts[0];
				String[] settings = parts[1].split(",");
				String[] notes = parts[2].split(",");
				/* Settings parsing */
				for(int t = 0; t< settings.length;t++){
					if(settings[t].startsWith("o")){
						octave = Integer.parseInt(settings[t].substring(2));
					} else if(settings[t].startsWith("d")){
						duration = Integer.parseInt(settings[t].substring(2));
					} else if(settings[t].startsWith("b")){
						bpm = Integer.parseInt(settings[t].substring(2));
					}
				}
				/* Note parsing */
				int noteDuration;
				int noteOctave;
				int note;
				
				/* Reset pattern data*/
				for(int t = 0; t < 64; t++){
					pattern.getData()[t][Pattern.TRACK_0_NOTE] = "000";
					pattern.getData()[t][Pattern.TRACK_0_VOLUME] = "000";		
					pattern.getData()[t][Pattern.TRACK_0_FX] = "000";	
					trackerEditor.table.setValueAt("000", t, Pattern.TRACK_0_NOTE);
					trackerEditor.table.setValueAt("000", t, Pattern.TRACK_0_VOLUME);						
				}
				
				for(int t = 0; t < notes.length && tickCounter < 64; t++){
					noteDuration = duration;
					noteOctave = octave;
					note = 0;
					boolean swExit = false;
					String noteData = notes[t].toUpperCase();
					//get note duration
					for(int i = 0; i < rttlNoteDuration.length && swExit == false;i++){
						if(noteData.startsWith(rttlNoteDuration[i])){
							swExit = true;
							noteDuration = noteTick[i];
							noteData = noteData.substring(rttlNoteDuration[i].length());
						}
					}
					//get note
					swExit = false;
					for(int i = 0; i < rtttlNotes.length && swExit == false;i++){
						if(noteData.startsWith(rtttlNotes[i])){
							swExit = true;
							note = rtttlNotesValue[i];
							if((noteData.length()- rtttlNotes[i].length()) > 0){
								noteData = noteData.substring(rtttlNotes[i].length());
							}else{
								noteData = "";
							}
						}
					}
					if(noteData != null && noteData.startsWith(".")){
						if(noteDuration > 1){
							noteDuration += (noteDuration / 2);
						}
						if(noteData.length() > 1){
							noteData = noteData.substring(1);
						}else{
							noteData = "";
						}
					}
					if(noteData != null && noteData.length() > 0){
						noteOctave = Integer.parseInt(noteData);
					}
					if(note == 0){
						pattern.getData()[tickCounter][Pattern.TRACK_0_NOTE] = "999";
						pattern.getData()[tickCounter][Pattern.TRACK_0_VOLUME] = "000";
						trackerEditor.table.setValueAt("999", tickCounter, Pattern.TRACK_0_NOTE);
						trackerEditor.table.setValueAt("000", tickCounter, Pattern.TRACK_0_VOLUME);						
					}else{
						note = (note - 1) + (12 * noteOctave);
						
						pattern.getData()[tickCounter][Pattern.TRACK_0_NOTE] = String.format("%03d", note);
						pattern.getData()[tickCounter][Pattern.TRACK_0_VOLUME] = "093";
						//FIXME: Should repaint with model not direct in component
						trackerEditor.table.setValueAt(String.format("%03d",note), tickCounter, Pattern.TRACK_0_NOTE);
						trackerEditor.table.setValueAt("093", tickCounter, Pattern.TRACK_0_VOLUME);
					}
					tickCounter += noteDuration;
					
				}
				
			}

		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("Error parsing RTTTL file.");
		}
	}
	
	
}
